package pt.compflow.tools;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import pt.compflow.flowontology.model.FLOWO;
import pt.compflow.storage.TripleStoreConnection;
import pt.ipp.isep.gecad.onto2flow.model.Onto2FlowModel;
import pt.ipp.isep.gecad.onto2flow.model.Onto2FlowModelFactory;

import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.Ontology;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;

/**
 * This class represents a domain/workflow ontology cache.
 * The use of Soft/Weak References makes the cache concept possible.
 * More info on Weak Refs: https://weblogs.java.net/blog/2006/05/04/understanding-weak-references
 * 
 * WeakReferences are automatically garbage collected if no strong refs are present;
 * SoftReferences stay for a while, being garbage collected according to memory needs.
 */
public class OntModelHelperManager {
	
//	private static final Logger LOG = LoggerFactory.getLogger(OntModelHelperManager.class);

	// saves soft references to OntModels
//	private Map<String, SoftReference<OntModelHelper>> ontoMap;
	
	// the ReferenceQueue will save OntModels collected by the garbage collector
//	private ReferenceQueue<OntModelHelper> deadReferenceQueue;
	// the revMap allows us to get the ontoMap key, after getting a dead reference in the queue
//	private Map<SoftReference<OntModelHelper>, String> revMap;
	
	public OntModelHelperManager() {
//		ontoMap = new HashMap<String, SoftReference<OntModelHelper>>();
//		revMap = new HashMap<SoftReference<OntModelHelper>, String>();
//		deadReferenceQueue = new ReferenceQueue<OntModelHelper>();
	}
	
	/**
	 * Removes garbage collected OntModels from the Manager
	 * These are, typically, OntModels without strong references to them
	 * which haven't been used for a while
	 */
	public void cleanup() {
//		Reference<? extends OntModelHelper> deadModel = null;
//		
//		while((deadModel = deadReferenceQueue.poll()) != null) {
//			String key = revMap.get(deadModel);
//			revMap.remove(deadModel);
//			ontoMap.remove(key);
//		}
	}

	/**
	 * Returns a cached or new (if not present in the cache) OntModel for the supplied
	 * ontology
	 * @param path path to the ontology (be it http, file; as supported by Jena)
	 * @return the OntModel
	 */
	public OntModelHelper get(TripleStoreConnection store, String jobUri, String workflowLocalName) {
//		cleanup();
		
		String path = jobUri + "_" + workflowLocalName;
//		if(ontoMap.containsKey(path)) {
//			LOG.debug("Returning Cached OntModelHelper: " + path);
////			OntModelHelper helper = ontoMap.get(path).get();
////			if(helper.getOntModel().get
//			return ontoMap.get(path).get();
//		}
//		
//		LOG.debug("Loading OntModelHelper to Cache: " + path);
		
		Onto2FlowModel model = Onto2FlowModelFactory.createModelForGraph(store.connectNamedGraph(path));
		OntModelHelper helper = new OntModelHelper(model);
				
		// deadReferenceQueue is sent so that it is notified when the model is garbage collected
//		SoftReference<OntModelHelper> ref = new SoftReference<OntModelHelper>(helper, deadReferenceQueue);
//		ontoMap.put(path, ref);
//		revMap.put(ref, path);

		return helper;		
	}	
		
	private static void copyModelAndImports(OntModel fromModel, Model toModel, Set<String> doneModels) {
		toModel.add(fromModel.getRawModel());
		for(String modelUri : fromModel.listImportedOntologyURIs()) {
//			String compareUri = modelUri;
//			if(!compareUri.endsWith("/")) compareUri += "/";
//			if(compareUri.compareTo(FLOWO.getURI()) != 0) {
			if(!doneModels.contains(modelUri)) {
				doneModels.add(modelUri);
				OntModel importModel = fromModel.getImportedModel(modelUri);
				System.out.println("Adding imported model " + modelUri);
				copyModelAndImports(importModel, toModel, doneModels);
			}
//			}
		}		
	}
	
	public OntModelHelper create(TripleStoreConnection store, String jobUri, String workflowLocalName, OntModel sourceModel) {
	
		String path = jobUri + "_" + workflowLocalName;
		
		Onto2FlowModel wfDefModel = Onto2FlowModelFactory.createModelForGraph(store.connectNamedGraph(path));
		
		Ontology ont = sourceModel.listOntologies().next();
		wfDefModel.add(wfDefModel.createResource("mainOntology"), FLOWO.hasUrl, wfDefModel.createLiteral(ont.getURI()));
		
		Set<String> doneModels = new HashSet<String>();
		doneModels.add(ont.getURI());
		copyModelAndImports(sourceModel, wfDefModel, doneModels);
		
		List<Statement> ontologyList = wfDefModel.listStatements(null, RDF.type, OWL.Ontology).toList();
		for(Statement stm : ontologyList) {
			Resource o = stm.getSubject();
			List<Statement> importList = o.listProperties(OWL.imports).toList();
			for(Statement stm2 : importList) {
				Resource i = stm2.getObject().asResource();
				String compareUri = i.getURI();
				if(!compareUri.endsWith("/")) compareUri += "/";
//				if(compareUri.compareTo(FLOWO.getURI()) != 0) {
				wfDefModel.remove(o, OWL.imports, i);
//				}
			}
		}
		
		OntModelHelper helper = new OntModelHelper(wfDefModel);
		
		// deadReferenceQueue is sent so that it is notified when the model is garbage collected
//		SoftReference<OntModelHelper> ref = new SoftReference<OntModelHelper>(helper, deadReferenceQueue);
//		ontoMap.put(path, ref);
//		revMap.put(ref, path);
		
		return helper;
	}
}
